package br.com.it4.framework.persistence.dao;

import java.lang.reflect.ParameterizedType;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;

import br.com.it4.framework.persistence.exception.PersistenceException;

/**
 * Classe de manipulação básica para uma determinada entidade.
 * 
 * @author Mozart Silva (mozart.vsilva@gmail.com)
 * 
 * @param <T>
 *            A entidade generica a ser manipulada na persistência.
 */
public abstract class BaseDAOImpl<T> implements BaseDAO<T> {

    /**
     * A entidade de gerenciamento da persistência.
     */
    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    protected EntityManager entityManager;

    /**
     * @return A entidade de gerenciamento da persistência.
     */
    protected EntityManager getEntityManager() {
        return this.entityManager;
    }

    /** {@inheritDoc} */
    public T add(T object) throws PersistenceException {
        try {
            this.entityManager.persist(object);
        } catch (Exception e) {
            throw new PersistenceException(e);
        }
        return object;
    }

    /** {@inheritDoc} */
    public T update(T object) throws PersistenceException {
        try {
            this.entityManager.merge(object);
        } catch (Exception e) {
            throw new PersistenceException(e);
        }
        return object;
    }

    /** {@inheritDoc} */
    public void remove(T object) throws PersistenceException {
        try {
            this.entityManager.remove(this.entityManager.merge(object));
        } catch (Exception e) {
            throw new PersistenceException(e);
        }
    }

    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    public T get(Object primaryKey) throws PersistenceException {
        T objectFound = null;

        try {
            objectFound = (T) this.entityManager.find(getEntityClass(), primaryKey);
        } catch (Exception e) {
            throw new PersistenceException(e);
        }

        return objectFound;
    }

    /**
     * @return A classe da entidade retornada.
     */
    @SuppressWarnings({ "rawtypes" })
    private Class getEntityClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class) parameterizedType.getActualTypeArguments()[0];
    }
}
